#include "common.h"
#include "index.h"
#include "lock.h"

static int read_dir_line(FILE *fp_entry,char *cacheDir,int curSiteId);
static int read_file_line(FILE *fp_entry,char *cacheDir,int curSiteId,int curPathId,int *pAtId);
static int write_index(char *cacheDir,struct index *pIndex,char *filename);
static int write_index_twobytes(char *cacheDir,struct index *pIndex,char twobytes[2]);

/**
 * build index for the data retrieved by getNames
 *
 * @argv[1] dataNames dir
 * @argc[2] dataIndex cache dir
 * @argv[3] dataIndex dir
 */
int main(int argc,char *argv[]) {

	pid_t p_rmdir;
	int ret;
	char buf[MAXLINE];
	int i;
	int fd_sites_formatted;
	int fd_lock;
	DIR *dataNamesDir;
	struct dirent *dataNamesEntry;
	FILE *fp_sites_formatted;
	int siteId = 0;

	if(argc != 4) {
		MSG("argc error:buildIndex");
		exit(EXIT_FAILURE);
	}

	p_rmdir = fork();
	if(p_rmdir < 0) {
		MSG("fork failed:remove dataIndex cache dir");
		exit(EXIT_FAILURE);
	}
	else if(p_rmdir == 0) {
		execlp("rm","rm",argv[2],"-rf",NULL);
	}
	else {
		if(wait(NULL) == -1) {
			MSG("wait failed:remove dataIndex cache dir");
			exit(EXIT_FAILURE);
		}
	}

	ret = mkdir(argv[2],0755);
	if(ret == -1) {
		MSG("mkdir failed:dataIndex cache dir");
		exit(EXIT_FAILURE);
	}

	snprintf(buf,MAXLINE,"%s/%s",argv[2],SITES_FORMATTED);
	fd_sites_formatted = open(buf,O_WRONLY|O_CREAT|O_EXCL,0644);
	if(fd_sites_formatted < 0) {
		MSG("creat failed:%s",buf);
		exit(EXIT_FAILURE);
	}
	if(close(fd_sites_formatted) < 0) {
		MSG("close failed:%s",buf);
		exit(EXIT_FAILURE);
	}
	
	snprintf(buf,MAXLINE,"%s/%s",argv[2],ATTR_DIR);
	ret = mkdir(buf,0755);
	if(ret == -1) {
		MSG("mkdir failed:%s",buf);
		exit(EXIT_FAILURE);
	}

	snprintf(buf,MAXLINE,"%s/%s",argv[2],PATH_DIR);
	ret = mkdir(buf,0755);
	if(ret == -1) {
		MSG("mkdir failed:%s",buf);
		exit(EXIT_FAILURE);
	}

	snprintf(buf,MAXLINE,"%s/%s",argv[2],INDEX_DIR);
	ret = mkdir(buf,0755);
	if(ret == -1) {
		MSG("mkdir failed:%s",buf);
		exit(EXIT_FAILURE);
	}

	for(i = 0; i < 256; i++) {
		snprintf(buf,MAXLINE,"%s/%s/%d",argv[2],INDEX_DIR,i);
		ret = mkdir(buf,0755);
		if(ret == -1) {
			MSG("mkdir failed:%s",buf);
			exit(EXIT_FAILURE);
		}
	}


	dataNamesDir = opendir(argv[1]);
	if(dataNamesDir == NULL) {
		MSG("opendir failed:%s",argv[1]);
		exit(EXIT_FAILURE);
	}

	snprintf(buf,MAXLINE,"%s/%s",argv[2],SITES_FORMATTED);
	fp_sites_formatted = fopen(buf,"w");
	if(fp_sites_formatted == NULL) {
		MSG("fopen failed:%s",buf);
		exit(EXIT_FAILURE);
	}

	chdir(argv[1]);
	while((dataNamesEntry = readdir(dataNamesDir)) != NULL) {
		if(!strcmp(dataNamesEntry->d_name,".") || !strcmp(dataNamesEntry->d_name,".."))
			continue;
		//printf("%s\n",dataNamesEntry->d_name);
		struct site site;
		FILE *fp_entry;

		site.siteId = siteId++;
		sscanf(dataNamesEntry->d_name,"%s" FILENAME_SEP "%hu" FILENAME_SEP 
						"%s" FILENAME_SEP "%s",
						site.ip_addr,&site.port,
						site.username,site.password);
		//printf("%d - %s\t%hu\t%s\t%s\n",site.siteId,site.ip_addr,site.port,site.username,site.password);
		fwrite(&site,sizeof(site),1,fp_sites_formatted);

		fp_entry = fopen(dataNamesEntry->d_name,"r");
		if(fp_entry == NULL) {
			MSG("fopen failed:%s",dataNamesEntry->d_name);
			continue;
		}

		if(read_dir_line(fp_entry,argv[2],site.siteId) != 0) {
			MSG("read_dir_line failed:%s",dataNamesEntry->d_name);
			fclose(fp_entry);
			continue;
		}


		fclose(fp_entry);
		run_log("one site indexed(%d):%s\n",site.siteId,dataNamesEntry->d_name);
	}
	chdir("..");

	fclose(fp_sites_formatted);

	/*
	if(unlink(argv[3]) < 0) {
		MSG("unlink failed:%s",argv[3]);
		exit(EXIT_FAILURE);
	}
	*/
	fd_lock = open(LOCK_DATAINDEX,O_RDWR|O_CREAT,0644);
	if(fd_lock < 0) {
		MSG("open failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}
	
	if(writew_lock(fd_lock,0,SEEK_SET,0) < 0) {
		MSG("writew_lock failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}

	p_rmdir = fork();
	if(p_rmdir < 0) {
		MSG("fork failed:remove dataIndex dir");
		exit(EXIT_FAILURE);
	}
	else if(p_rmdir == 0) {
		execlp("rm","rm",argv[3],"-rf",NULL);
	}
	else {
		if(wait(NULL) == -1) {
			MSG("wait failed:remove dataIndex dir");
			exit(EXIT_FAILURE); }
	}
	if(rename(argv[2],argv[3]) < 0) {
		MSG("rename failed:%s -> %s",argv[2],argv[3]);
		exit(EXIT_FAILURE);
	}

	if(un_lock(fd_lock,0,SEEK_SET,0) < 0) {
		MSG("un_lock failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}
	if(close(fd_lock) < 0) {
		MSG("close failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}

	run_log("buildIndex finished");
	exit(EXIT_SUCCESS);
}


static int read_dir_line(FILE *fp_entry,char *cacheDir,int curSiteId) {
	
	char buf[MAXLINE];
	int pathId = 0;
	int attrId = 0;
	FILE *fp_path;
	FILE *fp_path_map;
	char *ptr;
	struct path path;

	snprintf(buf,MAXLINE,"../%s/%s/%d",cacheDir,PATH_DIR,curSiteId);
	if((fp_path = fopen(buf,"a")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}

	snprintf(buf,MAXLINE,"../%s/%s/%d%s",cacheDir,PATH_DIR,curSiteId,PATH_MAP);
	if((fp_path_map = fopen(buf,"a")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}

	while(fgets(buf,MAXLINE,fp_entry) != NULL) {
		//ptr = strrchr(buf,':');
		//*ptr = 0;
		ptr = &buf[strlen(buf)-2];
		if(*ptr != ':')
			continue;

		*ptr = 0;

		path.pathId = pathId++;
		if((signed)(path.offset = ftell(fp_path_map)) == -1) {
			MSG("fwrite failed:%s/%s/%d%s",cacheDir,PATH_DIR,curSiteId,PATH_MAP);
			fclose(fp_path);
			fclose(fp_path_map);
			return -1;
		}
		//MSG("%d",path.offset);
		path.length = strlen(buf);

		fprintf(fp_path_map,"%s\n",buf);
		if(fwrite(&path,sizeof(path),1,fp_path) == 0) {
			MSG("ftell failed:%s/%s/%d",cacheDir,PATH_DIR,curSiteId);
			fclose(fp_path);
			fclose(fp_path_map);
			return -1;
		}
		
		if(read_file_line(fp_entry,cacheDir,curSiteId,path.pathId,&attrId) == -1) {
			MSG("read_file_line failed:siteId = %d\tpathId=%d",curSiteId,path.pathId);
			fclose(fp_path);
			fclose(fp_path_map);
			return -1;
		}

		run_log("one dir finished:%s",buf);

	}


	

	fclose(fp_path);
	fclose(fp_path_map);
	run_log("read_dir_line finished");

	return 0;
}

static int read_file_line(FILE *fp_entry,char *cacheDir,int curSiteId,int curPathId,int *pAtId) {
	char buf[MAXLINE];
	struct attr attr;
	struct index index;
	FILE *fp_attr;
	FILE *fp_attr_map;
	char *filenameptr;
	char filename[MAXLINE];
	
	snprintf(buf,MAXLINE,"../%s/%s/%d",cacheDir,ATTR_DIR,curSiteId);
	if((fp_attr = fopen(buf,"a")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}
		
	snprintf(buf,MAXLINE,"../%s/%s/%d%s",cacheDir,ATTR_DIR,curSiteId,PATH_MAP);
	if((fp_attr_map = fopen(buf,"a")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}

	while(1) {
		if(fgets(buf,MAXLINE,fp_entry) == NULL) {
			MSG("read_file_line reach EOF:siteId = %d",curSiteId);
			return 0;
		}

		if(strcmp(buf,"\n") == 0)
			break;

		filenameptr = filename_position(buf);
		if(filenameptr == NULL)
			continue;

		if(!strcmp(filenameptr,".\r\n") || !strcmp(filenameptr,"..\r\n"))
			continue;


		//MSG("buf=(%s)",buf);
		bzero(&attr,sizeof(attr));
		if(set_attr(&attr,*pAtId,buf) == -1) {
			MSG("set_attr failed:%s",buf);
			fclose(fp_attr);
			fclose(fp_attr_map);
			return -1;
		}
		
		strncpy(filename,filename_position(buf),MAXLINE);
		filename[strlen(filename)-2] = 0;
		//printf("filename=(%s)\n",filename);

		if((signed)(attr.offset = ftell(fp_attr_map)) == -1) {
			MSG("ftell failed:%s/%s/%d%s",cacheDir,ATTR_DIR,attr.attrId,PATH_MAP);
			fclose(fp_attr);
			fclose(fp_attr_map);
			return -1;
		}
		fprintf(fp_attr_map,"%s\n",filename);
		attr.length = strlen(filename);

		//printf("%s - (%d)datetime=%s\tfileSize=%llu\n\n",filename_position(buf),attr.attrId,attr.datetime,attr.fileSize);

		//printf("filename=%s\n",filename_position(buf));


		bzero(&index,sizeof(index));
		index.siteId = curSiteId;
		index.pathId = curPathId;
		index.attrId = attr.attrId;

		if(write_index(cacheDir,&index,filename) == -1) {
			MSG("write_index failed:siteId = %u\tpathId = %u\tattrId = %u",
						curSiteId,curPathId,attr.attrId);
			fclose(fp_attr);
			fclose(fp_attr_map);
			return -1;
		}
		
		if(fwrite(&attr,sizeof(attr),1,fp_attr) == 0) {
			MSG("fwrite failed:siteId = %u\tpathId = %u\tattrId = %u",
						curSiteId,curPathId,attr.attrId);
			fclose(fp_attr);
			fclose(fp_attr_map);
			return -1;
		}
		//printf("%d\t%u\t%u\n",attr.attrId,attr.offset,attr.length);

		//*pAtId = *pAtId + 1;
		(*pAtId)++;
	}

	fclose(fp_attr);
	fclose(fp_attr_map);
	run_log("read_file_line finished:siteId = %d\tpathId=%d",curSiteId,curPathId);
	return 0;

}

static int write_index(char *cacheDir,struct index *pIndex,char *filename) {
	
	//printf("(%d)(%s)\n",strlen(filename),filename);
	int i;
	int length;

	length = strlen(filename);

	for(i = 0; i < length; i++) {
		if(i == (length-1))
			break;
		if((unsigned char)filename[i] >= 128 && 
				(unsigned char)filename[i+1] >= 128) {
			if(write_index_twobytes(cacheDir,pIndex,&filename[i]) == -1) {
				MSG("write_index_twobytes failed");
				return -1;
			}
			i++;
		}
		else if((unsigned char)filename[i] < 128 && 
				(unsigned char)filename[i+1] < 128) {
			if(write_index_twobytes(cacheDir,pIndex,&filename[i]) == -1) {
				MSG("write_index_twobytes failed");
				return -1;
			}
		}
		else if((unsigned char)filename[i] < 128 && 
				(unsigned char)filename[i+1] >= 128) {
			continue;
		}

	}
	run_log("(siteId=%u pathId=%u attrId=%u):%s",
			pIndex->siteId,pIndex->pathId,pIndex->attrId,filename);
	
	return 0;
}

static int write_index_twobytes(char *cacheDir,struct index *pIndex,char twobytes[2]) {
	unsigned char c1,c2;
	FILE *fp_index;
	char buf[MAXLINE];

	c1 = twobytes[0];
	c2 = twobytes[1];

	//printf("%u\t%u\t%u\t%hhu\t%hhu\n",pIndex->siteId,pIndex->pathId,pIndex->attrId,c1,c2);
	
	snprintf(buf,MAXLINE,"../%s/%s/%hhu/%hhu",cacheDir,INDEX_DIR,c1,c2);
	//printf("%s\n",buf);
	if((fp_index = fopen(buf,"a")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}
	if(fwrite(pIndex,sizeof(*pIndex),1,fp_index) == 0) {
		MSG("fwrite failed:%s",buf);
		return -1;
	}

	fclose(fp_index);
	

	return 0;
}
